package com.strive.common.redis.lock;

import com.strive.common.constant.CommonConstant;
import com.strive.common.exception.LockException;
import com.strive.common.lock.DistributedLock;
import com.strive.common.lock.ZLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: bingcun.chen
 * @Date: 2022/2/21 13:55
 * @Version 1.0
 */
@ConditionalOnClass(RedissonClient.class)
@ConditionalOnProperty(prefix = "strive.lock",name = "lockerType",havingValue = "REDIS",matchIfMissing = true)
public class RedissonDistributedLock implements DistributedLock {

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public ZLock lock(String key, long leaseTime, TimeUnit unit, boolean isFair){
        ZLock zLock = getLock(key,isFair);
        RLock lock = (RLock)zLock.getLock();
        lock.lock(leaseTime,unit);
        return zLock;
    }
    @Override
    public ZLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws InterruptedException {
        ZLock zLock = getLock(key,isFair);
        RLock lock = (RLock)zLock.getLock();
        if(lock.tryLock(waitTime,leaseTime,unit)){
            return zLock;
        }
        return null;
    }

    @Override
    public void unlock(Object lock) throws Exception {
        if(lock != null){
            if(lock instanceof RLock){
                RLock rLock = (RLock)lock;
                if(rLock.isLocked()){
                    rLock.unlock();
                }
            }else {
                throw new LockException("requires RLock type");
            }
        }
    }

    /**
     * 获取锁方法
     * @param key
     * @param isFair
     * @return
     */
    private ZLock getLock(String key, boolean isFair) {
        RLock lock;
        if(isFair){
            lock = redissonClient.getFairLock(CommonConstant.LOCK_KEY_PREFIX + ":" + key);
        }else {
            lock = redissonClient.getLock(CommonConstant.LOCK_KEY_PREFIX + ":" + key);
        }
        return new ZLock(lock,this);
    }
}
